package http

import (
	"encoding/base64"
	"fmt"
	"io"
	"net/http"
	"regexp"
	"strings"
	"time"

	"engine/log"
	"gitee.com/ameise84/e3utils/bytes_buffer"
	"gitee.com/ameise84/e3utils/uuid"
	"github.com/gin-contrib/cors"
	"github.com/gin-gonic/gin"
	"github.com/pkg/errors"
)

var (
	_gHttpEngine    *gin.Engine
	_gShiftBuffPool bytes_buffer.Pool[*bytes_buffer.ShiftBuffer]
	_gLogOkRsp      bool
)

func init() {
	_gShiftBuffPool = bytes_buffer.NewShiftBufferPool(2048, 0, true)
	gin.DefaultWriter = io.Discard
	gin.SetMode(gin.ReleaseMode)
	_gHttpEngine = gin.New()
	_gHttpEngine.Use(cors.Default(), recovery)
	_gLogOkRsp = false
}

func recovery(c *gin.Context) {
	defer func() {
		if x := recover(); x != nil {
			log.GetLogger().Error("OnPanic").Err(errors.New(fmt.Sprintf("%+v", x))).Println()
			c.AbortWithStatus(http.StatusInternalServerError)
		}
	}()
	c.Next()
}

func initReq(c *gin.Context) {
	log.GetLogger().Info("initReq").Println()
	id := uuid.NewXid()
	clientIP := c.ClientIP()
	method := c.Request.Method

	rspWr := &ResponseWriter{
		ResponseWriter: c.Writer,
		OldRead:        c.Request.Body,
		RspBuf:         _gShiftBuffPool.Get(),
	}
	url := c.Request.Host + c.Request.URL.String()
	c.Writer = rspWr
	reqBody, _ := io.ReadAll(rspWr.OldRead)
	if len(reqBody) > 0 {
		c.Request.Body = io.NopCloser(bytes_buffer.Warp(reqBody))
	}
	start := time.Now()
	c.Next()
	duration := time.Since(start)
	statusCode := c.Writer.Status()
	rspBody, _ := rspWr.RspBuf.Peek()
	if rspWr.Status() != http.StatusOK {
		log.GetLogger().Warn("http response not ok").Str("id", id).Str("method", method).Str("clientIP", clientIP).Int("status", statusCode).Float64("cost time", 2, duration.Seconds()).Str("req", url+"?form_body="+base64.StdEncoding.EncodeToString(reqBody)).Str("rsp", base64.StdEncoding.EncodeToString(rspBody)).Println()
	} else if _gLogOkRsp {
		log.GetLogger().Info("http response ok").Str("id", id).Float64("cost time", 2, duration.Seconds()).Str("req", url+"?form_body="+base64.StdEncoding.EncodeToString(reqBody)).Str("rsp", base64.StdEncoding.EncodeToString(rspBody)).Println()
	}
	_gShiftBuffPool.Put(rspWr.RspBuf)
	c.Request.Body = rspWr.OldRead
	rspWr.RspBuf = nil
}

func onReq(c *gin.Context) {
	rc := takeRawContext()
	netActor, ok := _gActorServer.StartNetActor(rc.id)
	if !ok {
		return
	}
	rc.onInit(c, netActor)
	//protoc := ""
	//method := c.Request.Method
	//switch method {
	//case http.MethodGet:
	//	protoc = "Get"
	//case http.MethodHead:
	//	protoc = "Head"
	//case http.MethodPost:
	//	protoc = "Post"
	//case http.MethodPut:
	//	protoc = "Put"
	//case http.MethodPatch:
	//	protoc = "Patch"
	//case http.MethodDelete:
	//	protoc = "Del"
	//case http.MethodConnect:
	//	protoc = "Connect"
	//case http.MethodOptions:
	//	protoc = "Options"
	//case http.MethodTrace:
	//	protoc = "Trace"
	//default:
	//}
	//protoc += transformPath(c.Request.URL.Path)
	rc.onRecv(c.Request.URL.Path)
	rc.wg.Wait()
	rc.onClosed()
	freeRawContext(rc)
}

func transformPath(path string) string {
	re := regexp.MustCompile(`[/_-](\w?)`)
	return re.ReplaceAllStringFunc(path, func(m string) string {
		return strings.ToUpper(m[1:])
	})
}
